import datetime
from decimal import Decimal
import aiomysql

from configs import config, errorCode
from models import execute, execute_with_commit, execute_many, execute_tuple,user,order
from views import BaseHandler
from utils.util import json_decode


class OrderSettlement(BaseHandler):

    @BaseHandler.auth
    async def post(self, *args, **kwargs):
        """
        ---
        tags:
          - 去结算
        summary:  从购物车去结算到填写订单页面接口
        description:
        parameters:
        - in: path
          name: user_token
          schema:
            type: string
            default: null
        requestBody:
          description:
          content:
            application/json:
                schema:
                  type: object
                  properties:
                    sku_ids:
                        type: array
        responses:
          '200':
            description: successful operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/OkModel'
          '400':
            description: error operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ErrorModel'
        """
        # 接收参数
        json_dict = json_decode(self.request.body)
        sku_ids = json_dict.get('sku_ids')

        # 查询用户的默认地址
        address = await order.get_user_default_address(6)
        if not address:
            address = None
        else:
            address = address[0]
        # 查询购物车中被勾选的商品
        sku_cart = await config.redis.hgetall('user_cart:%s' % self.current_user)
        new_sku_cart = {}
        for sku_id in sku_ids:
            if str(sku_id) in sku_cart:
                new_sku_cart[int(sku_id)] = sku_cart.get(str(sku_id))
        sku_ids = list(new_sku_cart.keys())
        if not sku_ids:
            return self.write_error(errorCode.NOT_GOODS, '非法参数')
        skus = await order.get_sku_info_from_sku_id(sku_ids)
        skus, total_count, total_amount = await self.generate_skus(skus, new_sku_cart)
        return self.response_data(address, skus, total_count, total_amount)

    async def generate_skus(self, skus, new_sku_cart):
        total_count = 0
        total_amount = 0
        for sku in skus:
            property_values = await order.get_property_from_sku_id(sku)
            sku['price'] = float(sku.get('price'))
            sku['count'] = new_sku_cart.get(sku.get('id'))
            sku['amount'] = sku.get('price') * int(new_sku_cart.get(int(sku.get('id'))))
            sku['default_image'] = config.IMG_PATH + sku['default_image']
            sku['property_value'] = property_values
            total_count += int(sku.get('count'))
            total_amount += sku.get('amount')
        return skus, total_count, total_amount

    def response_data(self, address, skus, total_count, total_amount):
        context = {
            'address': address,
            'payment_method': config.payment_method,
            'skus': skus,
            'total_count': total_count,
            'total_amount': total_amount,
            'commit_order_url': self.reverse_url('commit_order_by_cart')
        }
        return self.write_json(data=context)


class NowBuy(BaseHandler):

    @BaseHandler.auth
    async def post(self, *args, **kwargs):
        """
        ---
        tags:
          - 立即购买
        summary: 从商品立即购买到填写订单页面接口
        description:
        requestBody:
          description: spu_id的值只能传5到8之间
          content:
            application/json:
                schema:
                  type: object
                  properties:
                    spu_id:
                      type: integer
                    count:
                        type: integer
                    property_values:
                        type: array
        responses:
          '200':
            description: successful operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/OkModel'
          '400':
            description: error operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ErrorModel'
        """
        # 接收参数
        json_dict = json_decode(self.request.body)
        spu_id = json_dict.get('spu_id')
        count = json_dict.get('count')
        property_values = json_dict.get('property_values')

        # 校验参数
        if not all([spu_id, count, property_values]):
            return self.write_error(errorCode.INVALID_ARGS, desc='缺少必要参数')

        spu = await order.query_spu_is_exist(spu_id)
        if not spu:
            return self.write_error(errorCode.INVALID_ARGS, desc='参数spu_id不存在')
        try:
            count = int(count)
        except Exception as e:
            return self.write_error(errorCode.INVALID_ARGS, desc='参数count错误')

        # 修改功能
        # 通过spu_id获取所有它下面的sku_id

        sku_ids = await order.get_sku_from_spu(spu_id)

        # 把sku_ids解析成一个列表
        new_sku_ids = []
        for i in sku_ids:
            new_sku_ids.append(i['id'])
        sku_id_count = len(new_sku_ids)

        # 判断property参数有没有少或多
        res = await order.query_spu_property_count(sku_id_count, new_sku_ids)
        if len(property_values) != int(res[0].get('total')):
            return self.write_error(errorCode.INVALID_ARGS, desc='缺少必要参数')

        # 通过前传来的spu_id和property属性来查到唯一的sku_id
        sku_set = await self.query_sku_from_spu_property(property_values, sku_id_count, new_sku_ids)
        try:
            sku_id = list(sku_set)[0]
        except Exception as e:
            self.logger.error(e)
            return self.write_error(errorCode.NOT_GOODS, '商品不存在')

        # 查询用户的默认地址
        address = await order.get_user_default_address(6)
        if not address:
            address = None
        else:
            address = address[0]
        sku = await order.get_sku_info_from_sku_id(sku_id)
        sku = sku[0]
        sku, total_count, total_amount = await self.generate_sku(sku, count)
        return self.response_data(address, sku, total_count, total_amount)

    async def query_sku_from_spu_property(self, property_values, sku_id_count, new_sku_ids):
        sku_set = []
        for i in property_values:
            sku_list = []
            new_sku_ids.append(i)
            res = await order.query_sku_from_property_title_skus(sku_id_count, new_sku_ids)
            for j in res:
                sku_list.append(j['sku_id'])
            if not sku_set:
                sku_set = set(sku_list)
            else:
                sku_set = sku_set & set(sku_list)
            del new_sku_ids[-1]
        return sku_set

    async def generate_sku(self, sku, count):
        total_count = 0
        total_amount = 0
        property_values = await order.get_property_from_sku_id(sku)
        sku['price'] = float(sku.get('price'))
        sku['count'] = int(count)
        sku['amount'] = sku.get('price') * int(count)
        sku['default_image'] = config.IMG_PATH + sku['default_image']
        sku['property_value'] = property_values
        total_count += sku.get('count')
        total_amount += sku.get('amount')
        return sku, total_count, total_amount

    def response_data(self, address, sku, total_count, total_amount):
        context = {
            'address': address,
            'payment_method': config.payment_method,
            'skus': [sku],
            'total_count': total_count,
            'total_amount': total_amount,
            'commit_order_url': self.reverse_url('commit_order_by_spu')
        }
        return self.write_json(data=context)


class CommitOrderByCart(BaseHandler):

    @BaseHandler.auth
    async def post(self, *args, **kwargs):
        """
        ---
        tags:
          - 提交订单
        summary:  生成订单
        description:  这个接口是从购物车页面从结算到填写订单后  当提交订单时访问的接口
        parameters:
        requestBody:
          description:
          content:
            application/json:
                schema:
                  type: object
                  properties:
                    sku_ids_count:
                        type: dict
                    address_id:
                        type: integer
                    payment_method:
                        type: integer
        responses:
          '200':
            description: successful operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/OkModel'
          '400':
            description: error operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ErrorModel'
        """

        # 接收参数
        json_dict = json_decode(self.request.body)
        address_id = json_dict.get('address_id')
        sku_ids_count = json_dict.get('sku_ids_count')
        payment_method = json_dict.get('payment_method')
        if not sku_ids_count:
            self.write_error(errorCode.INVALID_ARGS, '缺少参数')
        sku_ids = sku_ids_count.keys()
        try:
            if int(payment_method) not in config.payment_method.keys():
                return self.write_error(errorCode.INVALID_ARGS, '参数payment_method非法')
        except Exception as e:
            self.logger.error(e)
            return self.write_error(errorCode.DB_ERR, '参数payment_method非法')
        try:
            address = await execute('select * from address where id = %s', address_id)
            if not address:
                return self.write_error(errorCode.INVALID_ARGS, '参数address_id错误或为空')
        except Exception as e:
            self.logger.error(e)
            return self.write_error(errorCode.DB_ERR, '获取数据错误,请重新发送')

        # 获取订单编号： 时间+user_id
        order_id = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + ('%09d' % self.current_user)
        async with config.pool.acquire() as conn:
            async with conn.cursor(aiomysql.DictCursor) as cursor:
                await conn.begin()
                # row = await execute_with_commit(
                #     '''
                #     insert into `order`(order_id, uid, price, address_id, status, pay_method) values (%s, %s, %s, %s, %s, %d)
                #     ''' % (order_id, 9998, Decimal(0.00), address_id, 0, int(payment_method))
                # )
                try:
                    await cursor.execute(
                        '''
                                    insert into `order`(order_id, uid, price, address_id, status, pay_method) values (%s, %s, %s, %s, %s, %d)
                                    ''' % (order_id, self.current_user, Decimal(0.00), address_id, 0, int(payment_method))
                    )
                    # 未付款时要倒计时
                    await config.redis.setex(order_id, 3600*1, 0)
                    # 保存订单商品信息（多）
                    sku_cart = await config.redis.hgetall('user_id')
                    new_sku_cart = {}
                    for sku_id in sku_ids:
                        new_sku_cart[int(sku_id)] = sku_cart.get(str(sku_id))
                    order_detail_list = []
                    total_count = 0
                    total_amount = Decimal(0.00)
                    for sku_id in sku_ids:
                        while True:
                            _, sku = await execute('select id, price, num from good_sku where id = %s', (sku_id,))
                            sku = sku[0]
                            # 获取要提交订单的商品的数量
                            sku_count = int(new_sku_cart.get(sku.get('id')))
                            # 判断商品数量是否大于库存， 如果大于，响应’库存不存在‘
                            if sku_count > sku.get('num'):
                                await conn.rollback()
                                return self.write_error(errorCode.NOT_ENOUGH_STOCK, '库存不足')
                            sku_num = int(sku.pop('num'))
                            sku_value_list = list(sku.values())
                            sku_value_list.append(order_id)
                            sku_value_list.append(sku_count)
                            order_detail_list.append(tuple(sku_value_list))
                            # sku 减库存 加销量
                            sku_num -= sku_count
                            row = await cursor.execute('update good_sku set num = %s  where id = %s and num = %s', (sku_num, sku.get('id'), sku_num+sku_count))
                            if row == 0:
                                order_detail_list.pop()
                                continue
                            total_count += sku_count
                            total_amount += sku_count * sku.get('price')
                            break
                    await cursor.execute('update `order` set price = %s where order_id = %s', (total_amount, order_id))
                    await cursor.executemany('insert into order_detail(sku_id, price, oid, num) values (%s,%s,%s,%s)', order_detail_list)
                    # 订单生成后删除购物车里面的sku_id和对应的count
                except Exception as e:
                    await conn.rollback()
                    return self.write_error(errorCode.OTHER_ERR, '未知错误')
                await conn.commit()
        await config.redis.hdel('user_cart:%s' % self.current_user, *list(sku_ids))
        return self.write_json(data={'order_id': order_id})


class CommitOrderBySpu(BaseHandler):

    @BaseHandler.auth
    async def post(self, *args, **kwargs):
        """
        ---
        tags:
          - 提交订单
        summary:  生成订单
        description: 这个接口是从商品立即购买到填写订单后  当提交订单时访问的接口
        parameters:
        requestBody:
          description:
          content:
            application/json:
                schema:
                  type: object
                  properties:
                    sku_ids_count:
                        type: dict
                    address_id:
                        type: integer
                    payment_method:
                        type: integer
        responses:
          '200':
            description: successful operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/OkModel'
          '400':
            description: error operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ErrorModel'
        """

        # 接收参数
        json_dict = json_decode(self.request.body)
        address_id = json_dict.get('address_id')
        sku_ids_count = json_dict.get('sku_ids_count')
        payment_method = json_dict.get('payment_method')
        if not sku_ids_count:
            self.write_error(errorCode.INVALID_ARGS, '缺少参数')
        sku_ids = sku_ids_count.keys()
        try:
            if int(payment_method) not in config.payment_method.keys():
                return self.write_error(errorCode.INVALID_ARGS, '参数payment_method非法')
        except Exception as e:
            self.logger.error(e)
            return self.write_error(errorCode.DB_ERR, '参数payment_method非法')
        try:
            address = await execute('select * from address where id = %s', address_id)
            if not address:
                return self.write_error(errorCode.INVALID_ARGS, '参数address_id错误或为空')
        except Exception as e:
            self.logger.error(e)
            return self.write_error(errorCode.DB_ERR, '获取数据错误,请重新发送')

        # 获取订单编号： 时间+user_id
        order_id = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + ('%09d' % self.current_user)
        async with config.pool.acquire() as conn:
            async with conn.cursor(aiomysql.DictCursor) as cursor:
                await conn.begin()
                try:
                    await cursor.execute(
                        '''
                        insert into `order`(order_id, uid, price, address_id, status, pay_method) values (%s, %s, %s, %s, %s, %d)
                        ''' % (order_id, self.current_user, Decimal(0.00), address_id, 0, int(payment_method))
                    )
                    # 未付款时要倒计时
                    await config.redis.setex(order_id, 3600*1, 0)
                    # 保存订单商品信息（多）
                    # try:
                    #     _, skus = await execute('select id, price, num from good_sku where id in (%s)' % ','.join(['%s']*len(sku_ids)), tuple(sku_ids))
                    # except Exception as e:
                    #     self.logger.error(e)
                    #     await conn.rollback()
                    #     await cursor.close()
                    #     conn.close()
                    #     return self.write_error(errorCode.DB_ERR, '1数据操作有误,请重新操作')
                    order_detail_list = []
                    total_count = 0
                    total_amount = Decimal(0.00)
                    # for sku in skus:
                    for sku_id in sku_ids:
                        while True:
                            _, sku = await execute('select id, price, num from good_sku where id = %s', (sku_id, ))
                            sku = sku[0]
                            # 获取要提交订单的商品的数量
                            sku_count = int(sku_ids_count.get(str(sku.get('id'))))
                            # 判断商品数量是否大于库存， 如果大于，响应’库存不存在‘
                            if sku_count > sku.get('num'):
                                await conn.rollback()
                                return self.write_error(errorCode.NOT_ENOUGH_STOCK, '库存不足')
                            sku_num = int(sku.pop('num'))
                            sku_value_list = list(sku.values())
                            sku_value_list.append(order_id)
                            sku_value_list.append(sku_count)
                            order_detail_list.append(tuple(sku_value_list))
                            # sku 减库存 加销量
                            sku_num -= sku_count
                            row = await cursor.execute('update good_sku set num = %s  where id = %s and num = %s', (sku_num, sku.get('id'), sku_num+sku_count))
                            if row == 0:
                                order_detail_list.pop()
                                continue
                            total_count += sku_count
                            total_amount += sku_count * sku.get('price')
                            break
                    await cursor.execute('update `order` set price = %s where order_id = %s', (total_amount, order_id))
                    await cursor.executemany('insert into order_detail(sku_id, price, oid, num) values (%s,%s,%s,%s)', order_detail_list)

                except Exception as e:
                    await conn.rollback()
                    print(e)
                    return self.write_error(errorCode.OTHER_ERR, '未知错误')
                await conn.commit()
        return self.write_json(data={'order_id': order_id})



class MyOrder(BaseHandler):

    # @BaseHandler.auth
    async def get(self, order_status):
        """
        ---
        tags:
          - 商品订单列表
        summary: 订单状态 /-1->已取消/0->待付款/1->待发货/2->待收货/3->已完成/
        description:  订单全部/待付款/待发货/待收货/已取消 都是访问这个接口, （重点）在路径参数后面加相应的状态码， 全部订单不需要加状态码
        parameters:
          - name: 订单的状态码
            in: path
            description:
            required: False
            schema:
              type: string
        responses:
          '200':
            description: successful operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/OkModel'
          '400':
            description: error operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ErrorModel'
        """
        # 检测待付款的订单有没有到期，到期了就取消订单
        if not order_status or int(order_status) in (-1, 0):
            _, order_ids = await execute('select order_id from `order` where uid = %s and status = %s', (self.current_user, 0))
            cancel_order_id = []
            for order_id in order_ids:
                if await config.redis.ttl(order_id.get('order_id')) == -2:
                    cancel_order_id.append((order_id, ))
            if cancel_order_id:
                await execute_many('update `order` set status = -1 where order_id = %s', cancel_order_id)
                # 查询此订单的所有商品对应的num数量
                _, skus_id = await execute_tuple('select num, sku_id from order_detail where oid in (%s)' % ','.join(['%s']*len(cancel_order_id)), (cancel_order_id,))
                # 把对应的数量回到对应的sku商品中的库存中
                await execute_many('update good_sku set num = num + %s where id = %s', list(skus_id))
        if not order_status:
            _, orders = await execute('select order_id, price, status from `order` where uid = %s', (self.current_user, ))
        else:
            _, orders = await execute('select order_id, price, status from `order` where uid = %s and status = %s', (self.current_user, order_status))
        if not orders:
            return self.write_json()
        # 获取订单中所有商品的详细信息
        order_ids = []
        for order in orders:
            order_ids.append(order.get('order_id'))
        _, skus = await execute('select oid, sku_id, price, num from order_detail where oid in (%s)' % ','.join(['%s']*len(order_ids)), tuple(order_ids))
        # 获取商品的图片
        sku_ids = []
        for sku in skus:
            sku_ids.append(sku.get('sku_id'))
        _, skus_img = await execute('select id, title, default_image from good_sku where id in (%s)' % ','.join(['%s']*len(sku_ids)), tuple(sku_ids))
        # 获取商品的属性值
        _, sku_propertys = await execute('select sku_id, title from property_value where sku_id in (%s)' % ','.join(['%s']*len(sku_ids)), tuple(sku_ids))
        new_sku_property = {}
        for sku_property in sku_propertys:
            new_sku_property.setdefault(sku_property.get('sku_id'), []).append(sku_property.get('title'))
        # 把商品图片skus_img, new_sku_property和skus合并
        for sku in skus:
            sku['price'] = float(sku['price'])
            sku['property'] = new_sku_property.get(sku.get('sku_id'))
            for sku_img in skus_img:
                if sku_img.get('id') == sku.get('sku_id'):
                    sku['title'] = sku_img.get('title')
                    sku['default_image'] = config.IMG_PATH + sku_img.get('default_image')
                    break
        # 把订单orders和skus合并
        for order in orders:
            order['price'] = float(order['price'])
            for sku in skus:
                if sku.get('oid') == order.get('order_id'):
                    order.setdefault('skus', []).append(sku)
        return self.write_json(data=orders)


class OrderDetail(BaseHandler):

    @BaseHandler.auth
    async def get(self):
        """
        ---
        tags:
          - 订单详情
        summary: 进入订单详情
        description:
        parameters:
          - name: order_id
            in: query
            description: 订单编号
            required: true
            schema:
              type: string
        responses:
          '200':
            description: successful operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/OkModel'
          '400':
            description: error operation
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ErrorModel'
        """
        # 去获取订单编号
        order_id = self.get_query_argument('order_id', None)
        if not order_id:
            self.write_error(errorCode.INVALID_ARGS, '缺少参数')
        # 检测订单有没有过期，过期了就改状态为取消状态（-1）
        surplus_time = await config.redis.ttl(order_id)
        if surplus_time in (-1, -2):
            await execute_with_commit('update `order` set status = -1 where order_id = %s', (order_id,))
        # 查询此订单的所有商品对应的num数量
        _, skus_id = await execute_tuple('select num, sku_id from order_detail where oid = %s', (order_id,))
        # 把对应的数量回到对应的sku商品中的库存中
        await execute_many('update good_sku set num = num + %s where id = %s', list(skus_id))

        # 去数据库获取订单的基本信息
        _, order = await execute('select order_id, price, status, address_id, pay_method, add_time from `order` where order_id = %s', (order_id, ))
        _, address = await execute('select `name`, phone, area, detail from address where id = %s', (order[0].get('address_id'), ))
        _, order_skus = await execute('select sku_id, price, num from order_detail where oid = %s', (order_id, ))
        # 获取订单sku商品的属性
        sku_ids = []
        for order_sku in order_skus:
            sku_ids.append(order_sku.get('sku_id'))
        _, sku_propertys = await execute('select sku_id, title from property_value where sku_id in (%s)' % ','.join(['%s']*len(sku_ids)), tuple(sku_ids))
        new_sku_property = {}
        for sku_property in sku_propertys:
            new_sku_property.setdefault(sku_property.get('sku_id'), []).append(sku_property.get('title'))
        # 获取商品的图片和标题
        _, skus_img = await execute('select id, title, default_image from good_sku where id in (%s)' % ','.join(['%s']*len(sku_ids)), tuple(sku_ids))
        # 把sku商品属性合并到order_skus中去
        for order_sku in order_skus:
            order_sku['price'] = float(order_sku['price'])
            order_sku['property'] = new_sku_property.get(order_sku.get('sku_id'))
            for sku_img in skus_img:
                if sku_img.get('id') == order_sku.get('sku_id'):
                    order_sku['title'] = sku_img.get('title')
                    order_sku['default_image'] = config.IMG_PATH + sku_img.get('default_image')
                    break
        order[0]['price'] = float(order[0]['price'])
        order[0]['add_time'] = order[0]['add_time'].strftime('%Y-%m-%d %H:%M:%S')
        context = {
            "address": address,
            "skus": order_skus,
            "order_info": order[0],
            "surplus_time": None if surplus_time in (-1, -2) else surplus_time
        }
        return self.write_json(data=context)


